/**
 * Project 1 COSC 671 Spring 2010
 * @author Hui Liu
 * @dueDate Feb. 14th, 2010
 * @purpose
 * The Stutter.c program takes a filename as an argument and then scans the words (character by character) to report any repeated (i.e., stuttered) words.
 * 1. Change the program from C to Java
 * 2. Format the program according to the Java Programming Style Guidelines and the Tips for Maintainable Java Code discussed and handed out in class
 * 3. Document your Java code in Javadoc format and generate the Javadoc API HTML files for your code
 *
*/
package task4;

import java.io.*;

// TODO: Auto-generated Javadoc
/**
 * The Class Stutter contains methods to scans the words (character by character)
 * from input source to report any repeated (i.e., stuttered) words.
 * <p>
 * In order to keep the pattern of the original C program, The Class Stutter use java.io.BufferedReader
 * to scan the words character by character. It also contains several private methods to implement the word manipulation.
 *  
 * @author Hui Liu
 * @see NewStutter
 */
public class Stutter {

	/**
	 * The main method takes filename as arguments and use the method stut of Class Stutter to
	 * scans the words (character by character) to report any repeated (i.e., stuttered) words.
	 * <p>
	 * If there is no arguments, the program read data from the standard input device; 
	 * otherwise the arguments is a list of input filenames, the program will read the file one by one to scan for repeated words.
	 * 
	 * @param args 	command-line arguments, a list of filenames to be read
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		BufferedReader in;	// source file 
		Stutter stutter = new Stutter();
		
		if (args.length == 0) {
			// read data from the standard input device
			in = new BufferedReader(new InputStreamReader(System.in));
			// find repeated words
			stutter.stut(in);
		} else {
			// read file one by one
			for (int i = 0; i < args.length; i++) {
				try {
					// read data from the file specified in the argument
					in = new BufferedReader(new FileReader(args[i]));
					stutter.stut(in);
					in.close();
				} catch (Exception e) {
					// failed to read the file, print error message
					System.out.println("stutter error: " + e.toString());
				}
			}

		}

	}

	

	/**
	 * Scans the words (character by character) from the input source to 
	 * report any repeated (i.e., stuttered) words by printing out "Repeated word on line %d: %s %s\n"
	 * 
	 * <p><b>Algorithm:</b></p>
	 * <pre>
	 * while not the end of the source file {
	 * 		read a character
	 * 		if the character is a line feed, 
	 * 			increase the line number
	 * 		if the character is a special delimiter
	 * 			if it's not at the beginning of current word {
	 * 				then it is the end of current word
	 * 				compare current word with previous word
	 * 				if they are the same
	 * 					print out
	 * 				else 
	 * 					copy it to previous word
	 * 			}
	 * 		else
	 * 			append the character to current word
	 * 	} end of while	 
	 *  </pre>
	 *  
	 * @param in the input source
	 *
	*/
	public void stut(BufferedReader in) {
		char[] currentWord = new char[100];
		char[] previousWord = new char[100];
		int lineNo = 1;	// current line number
		boolean isBeginOfWord = true;

		int c = 0;		// the character read from the source
		int i = 0;		// the indicator of the character in the word
		previousWord[0] = '\0';		//initialize the previous word to empty


		// while not the end of file
		while (c != -1) {
			// read a single character
			try {
				c = in.read();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			// if c is a line feed, increase the line number
			if ((char) c == '\n')
				lineNo++;
			
			// if c is a special char
			if (isSpecialChar((char) c)) {
				// if the special char is not at the beginning of the word
				if (!isBeginOfWord) {
					currentWord[i] = '\0';	// set the end of the word
					
					if (isSame(previousWord, currentWord))
						System.out.printf("Repeated word on line %d: %s %s\n",
								lineNo, arrayToString(previousWord),
								arrayToString(currentWord));
					else
						copyWord(currentWord, previousWord);

					// reset the beginning of the word
					isBeginOfWord = true;
					i = 0;
				}
			} else {
				// c is not a special char, then append c to current word
				isBeginOfWord = false;
				currentWord[i++] = (char) c;
			}
		}

		System.out.println();

	}


	
	/**
	 * Return true if the input character is a special char in { '\0', '\n', '\r', '\t', '	', ' ', ',', '.', '!', '-',
				'+', '=', ';', ':', '?', '&', '{', '}', '\\' }
	 * 
	 * @param c the character to be checked
	 * 
	 * @return true, if c is a special char
	 */
	private boolean isSpecialChar(char c) {
		final char[] DELIMITERS = { '\0', '\n', '\r', '\t', '	', ' ', ',', '.', '!', '-',
				'+', '=', ';', ':', '?', '&', '{', '}', '\\' };
		int nDELIMITERS = DELIMITERS.length;

		for (int i = 0; i < nDELIMITERS; i++)
			if (c == DELIMITERS[i])
				return true;

		return false;
	}


	
	/**
	 * Returns true if the two specified words are same to one another.
	 * The end of word is specified by '\0' in the array. 
	 * Two words are considered same if both words contain the same number of characters, 
	 * and all corresponding pairs of characters in the two words are equal. 
	 * In other words, two words are equal if they contain the same characters in the same order. 
	 * Also, two words references are considered equal if both are start with '\0'.
	 * 
	 * @param a one word to be tested for equality.
	 * @param b the other word to be tested for equality.
	 * 
	 * @return true if the two word are the same
	 */
	private boolean isSame(char[] a, char[] b) {
		int i = 0;

		// while not the end of the word
		while ((a[i] != '\0') && (b[i] != '\0')) {
			// if the ith letter of the word is different, return false
			if (a[i] != b[i])
				return false;
			
			i++;	// read next char
		}
		
		// if both array reach the end of word
		if ((a[i] == '\0') && (b[i] == '\0'))
			return true;
		else
			return false;

	}


	
	/**
	 * Copy word from source to target character by character
	 * 
	 * @param source the source word to be copied
	 * @param target the target word to copy to
	 */
	private void copyWord(char[] source, char[] target) {
		int i = 0;
		// while not the end of the source word
		while (source[i] != '\0') {
			target[i] = source[i];
			i++;
		}
		target[i] = '\0';	// mark the end of word
	}


	
	/**
	 * Returns a string representation of the contents of the specified array.
	 * <p>
	 * If using Array.toString to directly convert array to string, 
	 * the string's length will be the same of array's size. 
	 * The '\0' cannot cut off string in Java. 
	 * So this method created a new array with the exact length of the word,
	 * and then create a string from it.
	 * 
	 * @param source the array whose word string representation to return
	 * 
	 * @return a string representation of source word
	 */
	private String arrayToString(char[] source) {
		int i = 0;
		// calculate the length of the word
		while (source[i] != '\0') 
			i++;

		// create an array with the exact length of the word
		char[] target = new char[i];
		
		// copy the word to the new char array
		for (int j = 0; j < i; j++)
			target[j] = source[j];

		return new String(target);
	}

}
